// internal/service/connection_service.go
package service

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
	"libvirt.org/go/libvirt"

	"virt-manager-go/internal/domain"
)

type connectionService struct {
	connections map[string]*connectionWrapper
	mu          sync.RWMutex
	logger      *logrus.Logger

	// 健康检查
	healthCheckInterval time.Duration
	stopHealthCheck     chan struct{}
}

type connectionWrapper struct {
	id          string
	uri         string
	conn        *libvirt.Connect
	connType    string
	connectedAt time.Time
	lastPing    time.Time
	mu          sync.RWMutex
}

func NewConnectionService(logger *logrus.Logger) domain.ConnectionManager {
	svc := &connectionService{
		connections:         make(map[string]*connectionWrapper),
		logger:              logger,
		healthCheckInterval: 30 * time.Second,
		stopHealthCheck:     make(chan struct{}),
	}

	// 启动健康检查协程
	go svc.healthCheckLoop()

	return svc
}

// Connect 连接到 Libvirt 守护进程
func (s *connectionService) Connect(ctx context.Context, uri string, opts *domain.ConnectionOptions) (string, error) {
	s.logger.WithField("uri", uri).Info("Connecting to libvirt")

	if opts == nil {
		opts = &domain.ConnectionOptions{}
	}

	// 检查是否已存在相同的连接
	s.mu.RLock()
	for _, wrapper := range s.connections {
		if wrapper.uri == uri {
			s.mu.RUnlock()
			s.logger.WithField("conn_id", wrapper.id).Info("Reusing existing connection")
			return wrapper.id, nil
		}
	}
	s.mu.RUnlock()

	// 建立新连接
	var conn *libvirt.Connect
	var err error

	if opts.ReadOnly {
		conn, err = libvirt.NewConnectReadOnly(uri)
	} else {
		conn, err = libvirt.NewConnect(uri)
	}

	if err != nil {
		return "", fmt.Errorf("failed to connect to libvirt: %w", err)
	}

	// 验证连接
	alive, err := conn.IsAlive()
	if err != nil || !alive {
		conn.Close()
		return "", fmt.Errorf("connection is not alive: %w", err)
	}

	// 获取连接信息
	hostname, err := conn.GetHostname()
	if err != nil {
		hostname = "unknown"
	}

	connType, err := conn.GetType()
	if err != nil {
		connType = "unknown"
	}

	// 创建连接包装器
	connID := uuid.New().String()
	wrapper := &connectionWrapper{
		id:          connID,
		uri:         uri,
		conn:        conn,
		connType:    connType,
		connectedAt: time.Now(),
		lastPing:    time.Now(),
	}

	// 存储连接
	s.mu.Lock()
	s.connections[connID] = wrapper
	s.mu.Unlock()

	s.logger.WithFields(logrus.Fields{
		"conn_id":  connID,
		"uri":      uri,
		"type":     connType,
		"hostname": hostname,
	}).Info("Connected to libvirt successfully")

	return connID, nil
}

// Disconnect 断开连接
func (s *connectionService) Disconnect(ctx context.Context, connID string) error {
	s.mu.Lock()
	wrapper, exists := s.connections[connID]
	if !exists {
		s.mu.Unlock()
		return fmt.Errorf("connection not found: %s", connID)
	}
	delete(s.connections, connID)
	s.mu.Unlock()

	wrapper.mu.Lock()
	defer wrapper.mu.Unlock()

	if wrapper.conn != nil {
		if _, err := wrapper.conn.Close(); err != nil {
			s.logger.WithError(err).Warn("Error closing libvirt connection")
		}
		wrapper.conn = nil
	}

	s.logger.WithField("conn_id", connID).Info("Disconnected from libvirt")
	return nil
}

// GetConnection 获取连接
func (s *connectionService) GetConnection(connID string) (*libvirt.Connect, error) {
	s.mu.RLock()
	wrapper, exists := s.connections[connID]
	s.mu.RUnlock()

	if !exists {
		return nil, fmt.Errorf("connection not found: %s", connID)
	}

	wrapper.mu.RLock()
	defer wrapper.mu.RUnlock()

	if wrapper.conn == nil {
		return nil, fmt.Errorf("connection is closed")
	}

	// 检查连接是否存活
	alive, err := wrapper.conn.IsAlive()
	if err != nil || !alive {
		// 尝试重新连接
		s.logger.WithField("conn_id", connID).Warn("Connection is not alive, attempting to reconnect")
		return s.reconnect(wrapper)
	}

	return wrapper.conn, nil
}

// reconnect 尝试重新连接
func (s *connectionService) reconnect(wrapper *connectionWrapper) (*libvirt.Connect, error) {
	wrapper.mu.Lock()
	defer wrapper.mu.Unlock()

	// 先关闭旧连接
	if wrapper.conn != nil {
		wrapper.conn.Close()
	}

	// 建立新连接
	conn, err := libvirt.NewConnect(wrapper.uri)
	if err != nil {
		return nil, fmt.Errorf("failed to reconnect to libvirt: %w", err)
	}

	// 验证连接
	alive, err := conn.IsAlive()
	if err != nil || !alive {
		conn.Close()
		return nil, fmt.Errorf("reconnected connection is not alive: %w", err)
	}

	// 更新连接
	wrapper.conn = conn
	wrapper.lastPing = time.Now()
	wrapper.connectedAt = time.Now()

	s.logger.WithFields(logrus.Fields{
		"conn_id": wrapper.id,
		"uri":     wrapper.uri,
	}).Info("Reconnected to libvirt successfully")

	return wrapper.conn, nil
}

// ListConnections 列出所有连接
func (s *connectionService) ListConnections(ctx context.Context) ([]*domain.ConnectionInfo, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	infos := make([]*domain.ConnectionInfo, 0, len(s.connections))

	for _, wrapper := range s.connections {
		wrapper.mu.RLock()

		info := &domain.ConnectionInfo{
			ID:          wrapper.id,
			URI:         wrapper.uri,
			Type:        wrapper.connType,
			ConnectedAt: wrapper.connectedAt,
			Connected:   wrapper.conn != nil,
		}

		if wrapper.conn != nil {
			if hostname, err := wrapper.conn.GetHostname(); err == nil {
				info.Hostname = hostname
			}

			if version, err := wrapper.conn.GetVersion(); err == nil {
				info.Version = version
			}

			if libVersion, err := wrapper.conn.GetLibVersion(); err == nil {
				info.LibVersion = libVersion
			}

			if alive, err := wrapper.conn.IsAlive(); err == nil {
				info.Connected = alive
			}
		}

		wrapper.mu.RUnlock()
		infos = append(infos, info)
	}

	return infos, nil
}

// Ping 检查连接健康状态
func (s *connectionService) Ping(ctx context.Context, connID string) error {
	s.mu.RLock()
	wrapper, exists := s.connections[connID]
	s.mu.RUnlock()

	if !exists {
		return fmt.Errorf("connection not found: %s", connID)
	}

	wrapper.mu.Lock()
	defer wrapper.mu.Unlock()

	if wrapper.conn == nil {
		return fmt.Errorf("connection is closed")
	}

	alive, err := wrapper.conn.IsAlive()
	if err != nil {
		return fmt.Errorf("failed to check connection: %w", err)
	}

	if !alive {
		return fmt.Errorf("connection is not alive")
	}

	wrapper.lastPing = time.Now()
	return nil
}

// GetCapabilities 获取 hypervisor 能力
func (s *connectionService) GetCapabilities(ctx context.Context, connID string) (*domain.Capabilities, error) {
	conn, err := s.GetConnection(connID)
	if err != nil {
		return nil, err
	}

	capsXML, err := conn.GetCapabilities()
	if err != nil {
		return nil, fmt.Errorf("failed to get capabilities: %w", err)
	}

	// 解析 capabilities XML
	caps, err := s.parseCapabilities(capsXML)
	if err != nil {
		return nil, fmt.Errorf("failed to parse capabilities: %w", err)
	}

	return caps, nil
}

// healthCheckLoop 健康检查循环
func (s *connectionService) healthCheckLoop() {
	ticker := time.NewTicker(s.healthCheckInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			s.performHealthCheck()
		case <-s.stopHealthCheck:
			return
		}
	}
}

// performHealthCheck 执行健康检查
func (s *connectionService) performHealthCheck() {
	s.mu.RLock()
	connections := make([]*connectionWrapper, 0, len(s.connections))
	for _, wrapper := range s.connections {
		connections = append(connections, wrapper)
	}
	s.mu.RUnlock()

	for _, wrapper := range connections {
		wrapper.mu.RLock()
		conn := wrapper.conn
		connID := wrapper.id
		wrapper.mu.RUnlock()

		if conn == nil {
			continue
		}

		alive, err := conn.IsAlive()
		if err != nil || !alive {
			s.logger.WithFields(logrus.Fields{
				"conn_id": connID,
				"error":   err,
			}).Warn("Connection health check failed")

			// 可以选择自动重连或通知用户
			continue
		}

		wrapper.mu.Lock()
		wrapper.lastPing = time.Now()
		wrapper.mu.Unlock()
	}
}

// parseCapabilities 解析 capabilities XML（简化版本）
func (s *connectionService) parseCapabilities(capsXML string) (*domain.Capabilities, error) {
	// 这里应该使用 libvirtxml 解析，为简化示例，返回基础结构
	caps := &domain.Capabilities{
		Host: domain.HostCapabilities{
			UUID: "unknown",
			CPU: domain.CPUInfo{
				Arch:   "x86_64",
				Model:  "unknown",
				Vendor: "unknown",
			},
		},
		Guests: []domain.GuestCapabilities{
			{
				OSType:   "hvm",
				Arch:     "x86_64",
				Machines: []string{"pc", "q35"},
			},
		},
	}

	return caps, nil
}

// Stop 停止连接服务
func (s *connectionService) Stop() {
	close(s.stopHealthCheck)

	s.mu.Lock()
	defer s.mu.Unlock()

	for connID := range s.connections {
		if err := s.Disconnect(context.Background(), connID); err != nil {
			s.logger.WithError(err).Warn("Error disconnecting")
		}
	}
}
